﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Mime;
using System.Security.Claims;
using System.Text;
using Zhp.Auth.IService;
using Zhp.Auth.Service;
using Zhp.Common.Appsetting;
using Zhp.Common.Helper;
using Zhp.Model.Common;

namespace Zhp.Auth
{
    public static class AuthorizationSetup
    {
        public static void AddAuthorizationSetup(this IServiceCollection services)
        {
            var jwtOptions = services.GetOptions<JwtOptions>();
            var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey));
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

            services.AddAuthentication(options =>
            {
                options.AddScheme<CustomAuthenticationHandler>(UrlTokenDefaults.AuthenticationScheme, UrlTokenDefaults.AuthenticationDisplayScheme);
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingKey,
                    ValidateIssuer = true,
                    ValidIssuer = jwtOptions.Issuer, //发行人
                    ValidateAudience = true,
                    ValidAudience = jwtOptions.Audience, //订阅人
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromSeconds(0),//时间偏移量，过期了还能用
                    RequireExpirationTime = true
                };
                options.Events = new JwtBearerEvents
                {
                    // 添加读取 Token 的方式
                    OnMessageReceived = context =>
                    {
                        var httpRequest = context.HttpContext.Request;
                        var path = httpRequest.Path;
                        // SignalR获取token
                        // 判断请求是否包含 Authorization 参数，如果有就设置给 Token
                        string accessToken = httpRequest.Query["access_token"] + "";
                        if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub")))
                        {
                            // 设置 Token
                            context.Token = accessToken;
                        }

                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        //token校验后，校验是否在线
                        var onlineService = services.BuildServiceProvider().GetService<IOnlineService>();
                        if (onlineService == null)
                        {
                            throw new Exception("IOnlineService没有注册");
                        }
                        var httpContext = context.HttpContext;
                        var userId = context.Principal.FindFirstValue(ClaimTypes.Sid);
                        var tokenId = context.Principal.FindFirstValue(JwtRegisteredClaimNames.Jti);
                        var isOnline = onlineService.IsOnline(tokenId, userId);
                        if (!isOnline.IsOnline)
                        {
                            context.Fail(new SecurityTokenInvalidLifetimeException(isOnline.Message));
                        }
                        return Task.CompletedTask;
                    },
                    //401未授权
                    OnChallenge = context =>
                    {
                        context.HandleResponse();
                        context.Response.ContentType = MediaTypeNames.Application.Json;
                        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        var result = new ApiResult
                        {
                            Status = StatusCodes.Status401Unauthorized,
                            Message = "未授权！",
                            Url = context.Request.Path
                        };

                        if (context.AuthenticateFailure?.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            if (context.Request.Path == "/api/Login/RefreshLogin")
                            {
                                result.Status = 4001;
                                result.Message = "登录过期，请重新登录！";
                            }
                            else
                            {
                                result.Status = 4000;
                                result.Message = "登录过期，请刷新Token！";
                            }
                        }
                        if (context.AuthenticateFailure?.GetType() == typeof(SecurityTokenInvalidLifetimeException))
                        {
                            //IsOnline缓存过期会走这里，需要重新登录
                            result.Status = 4001;
                            result.Message = context.AuthenticateFailure.Message;
                        }
                        context.Response.WriteAsync(result.ToJson(true));
                        return Task.CompletedTask;
                    },
                    //403没权限
                    OnForbidden = context =>
                    {
                        context.Response.ContentType = MediaTypeNames.Application.Json;
                        context.Response.StatusCode = StatusCodes.Status403Forbidden;
                        var result = new ApiResult
                        {
                            Status = StatusCodes.Status403Forbidden,
                            Message = "没权限！",
                            Url = context.Request.Path
                        };
                        context.Response.WriteAsync(result.ToJson(true));
                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        return Task.CompletedTask;
                    }
                };
            })
            .Services.Configure<MvcOptions>(options =>
            {
                options.Filters.Add(new AuthorizeFilter("Policy"));
            });
            //.AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>(nameof(CustomAuthenticationHandler), _ => { })

            //增加权限判定策略
            //需要结合业务来完成验证
            //1. 添加策略
            //2. 需要自定义策略
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Policy", policyBuilder => policyBuilder
                .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                .AddRequirements(new AuthorizeRequirement(ClaimTypes.Role)));
                //.AddAuthenticationSchemes(nameof(CustomAuthenticationHandler))
            });
            services.AddTransient<IAuthorizationHandler, CustomAuthorizationHandler>();
            services.AddTransient<IJwtTokenService, JwtTokenService>();
            services.AddTransient<IOnlineService, OnlineService>();
        }
    }
}
